
#include "VideoDecoder.h"

VideoDecoder::VideoDecoder(FFmpegCallJava *callJava, PlayStatus *playStatus) : callJava(callJava),
                                                                               playStatus(
                                                                                       playStatus) {
    videoQueue = new DataQueue(playStatus);
    pthread_mutex_init(&codecMutex, NULL);
}

void *videoPlay(void *context) {
    // c 环境转 c++
    VideoDecoder *mVideoDecoder = (VideoDecoder *) context;
    while (mVideoDecoder->playStatus != NULL && !mVideoDecoder->playStatus->exit) {
        if (mVideoDecoder->playStatus->seek) {
            av_usleep((int) (int64_t) (0.01 * 1000000.0));
            continue;
        }
        if (mVideoDecoder->playStatus->pause) {
            // 参考 ffplay
            av_usleep((int) (int64_t) (0.01 * 1000000.0));
            continue;
        }
        // 加载中 队列还没数据
        if (mVideoDecoder->videoQueue->getQueueSize() == 0) {
            if (!mVideoDecoder->playStatus->isLoad) {
                mVideoDecoder->playStatus->isLoad = true;
                mVideoDecoder->callJava->onLoad(true);
                av_usleep((int) (int64_t) (0.01 * 1000000.0));
                continue;
            }
        }
        AVPacket *avPacket = av_packet_alloc();
        if (mVideoDecoder->videoQueue->getAvPacket(avPacket) != 0) {
            // 没获取到数据
            av_packet_free(&avPacket);
            av_free(avPacket);
            avPacket = NULL;
            continue;
        }
        // 视频解码 比较耗时  多线程加锁
        pthread_mutex_lock(&mVideoDecoder->codecMutex);
        if (avcodec_send_packet(mVideoDecoder->avCodecContext, avPacket) != 0) {
            // 送解码数据失败
            av_packet_free(&avPacket);
            av_free(avPacket);
            avPacket = NULL;
            pthread_mutex_unlock(&mVideoDecoder->codecMutex);
            continue;
        }
        AVFrame *avFrame = av_frame_alloc();

        if (avcodec_receive_frame(mVideoDecoder->avCodecContext, avFrame) != 0) {
            // 没有获取到解码数据
            av_frame_free(&avFrame);
            av_free(avFrame);
            avFrame = NULL;
            av_packet_free(&avPacket);
            av_free(avPacket);
            avPacket = NULL;
            pthread_mutex_unlock(&mVideoDecoder->codecMutex);
            continue;
        }
        //   LOGE("当前视频格式  %d",avFrame->format);
        // 需要转换
        AVFrame *pFrameYUV420P = av_frame_alloc();
        int num = av_image_get_buffer_size(
                AV_PIX_FMT_YUV420P,
                mVideoDecoder->avCodecContext->width,
                mVideoDecoder->avCodecContext->height,
                1);
        uint8_t *buffer = static_cast<uint8_t *>(av_malloc(num * sizeof(uint8_t)));
        av_image_fill_arrays(
                pFrameYUV420P->data,
                pFrameYUV420P->linesize,
                buffer,
                AV_PIX_FMT_YUV420P,
                mVideoDecoder->avCodecContext->width,
                mVideoDecoder->avCodecContext->height,
                1);
        SwsContext *sws_ctx = sws_getContext(
                mVideoDecoder->avCodecContext->width,
                mVideoDecoder->avCodecContext->height,
                mVideoDecoder->avCodecContext->pix_fmt,
                mVideoDecoder->avCodecContext->width,
                mVideoDecoder->avCodecContext->height,
                AV_PIX_FMT_YUV420P,
                SWS_BICUBIC, NULL, NULL, NULL);

        if (!sws_ctx) {
            av_frame_free(&pFrameYUV420P);
            av_free(pFrameYUV420P);
            av_free(buffer);
            pthread_mutex_unlock(&mVideoDecoder->codecMutex);
            continue;
        }
        sws_scale(
                sws_ctx,
                reinterpret_cast<const uint8_t *const *>(avFrame->data),
                avFrame->linesize,
                0,
                avFrame->height,
                pFrameYUV420P->data,
                pFrameYUV420P->linesize);

        double diff = mVideoDecoder->getFrameDiffTime(avFrame);
        //  通过diff 计算休眠时间
        int delay = mVideoDecoder->getDelayTime(diff) * 1000000;
        av_usleep(delay);

        //渲染
        mVideoDecoder->callJava->onRenderYUV(
                mVideoDecoder->avCodecContext->width,
                mVideoDecoder->avCodecContext->height,
                pFrameYUV420P->data[0],
                pFrameYUV420P->data[1],
                pFrameYUV420P->data[2]);

        av_frame_free(&pFrameYUV420P);
        av_free(pFrameYUV420P);
        av_free(buffer);
        sws_freeContext(sws_ctx);

        // 释放
        av_frame_free(&avFrame);
        av_free(avFrame);
        avFrame = NULL;
        av_packet_free(&avPacket);
        av_free(avPacket);
        avPacket = NULL;
        pthread_mutex_unlock(&mVideoDecoder->codecMutex);
    }
    pthread_exit(&mVideoDecoder->thread_video_play);
}

void VideoDecoder::play() {
// 创建播放线程
    pthread_create(&thread_video_play, NULL, videoPlay, this);
}

void VideoDecoder::pause() {
    playStatus->pause = true;
}

void VideoDecoder::resume() {
    playStatus->pause = false;
}


double VideoDecoder::getDelayTime(double diff) {
    //3ms 没有绝对的时间同步，时间控制在绝对值 3ms 内 ffplay 是4ms
    if (diff > 0.003) {
        // 视频休眠时间
        delayTime = delayTime * 2 / 3;// * 3/2;
        if (delayTime < defaultDelayTime / 2) {
            // 用户有所察觉
            delayTime = defaultDelayTime * 2 / 3;
        } else if (delayTime > defaultDelayTime * 2) {
            delayTime = defaultDelayTime * 2;
        }
    } else if (diff < -0.003) {
        //视频超前 休眠时间 相比于以前大一些
        delayTime = delayTime * 3 / 2;
        if (delayTime < defaultDelayTime / 2) {
            delayTime = defaultDelayTime * 2 / 3;
        } else if (delayTime > defaultDelayTime * 2) {
            delayTime = defaultDelayTime * 2;
        }
    }
    //视频加速时 视频延时加大，放慢视频
    if (diff >= 0.5) {
        delayTime = 0;
    } else if (diff <= -0.5) {
        delayTime = defaultDelayTime * 2;
    }
    // 音频太快了   视频怎么赶也赶不上 视频队列全部清空直接解析最新的
    if (diff >= 10) {
        videoQueue->clearAvPacket();
        delayTime = defaultDelayTime;
    }
    // 视频太快了音频赶不上时 情况音频队列
    if (diff <= -10) {
        audioDecoder->audioQueue->clearAvPacket();
        delayTime = defaultDelayTime;
    }
    return delayTime;
}

double VideoDecoder::getFrameDiffTime(AVFrame *avFrame) {
    // 先获取视频时间戳  处理之后
    double pts = avFrame->best_effort_timestamp;
    if (pts == AV_NOPTS_VALUE) {
        pts = 0;
    }

    // pts=pts * time_base.num / time_base.den;
    pts *= av_q2d(time_base);

    if (pts > 0) {
        clock = pts;
    }
    // 音频时间为主
    return audioDecoder->clock - clock;
}


void VideoDecoder::release() {
    //    队列
    if (videoQueue != NULL) {
        delete (videoQueue);
        videoQueue = NULL;
    }
    if (avCodecContext != NULL) {
        avcodec_close(avCodecContext);
        avcodec_free_context(&avCodecContext);
        avCodecContext = NULL;
    }
    if (playStatus != NULL) {
        playStatus = NULL;
    }
    if (callJava != NULL) {
        callJava = NULL;
    }
}

VideoDecoder::~VideoDecoder() {
    pthread_mutex_destroy(&codecMutex);

}



